import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @ClassName MemoryManagerGUI
 * @Description Java Memory Management
 * @Author spring
 * @Date 2024/11/04
 * @Version 1.0
 **/
public class MemoryManagerGUI extends JFrame {
    /**
     * 这一段只要是将 GUI 图形化界面进行相关的引入
     */
    // 引入 GUI 图形化界面
    // 文本 图形化 GUI
    private JTextField inputFilePathField;
    private JTextField outputFilePathField;
    // 文本框按钮
    private JButton selectInputFileButton;
    private JButton selectOutputFileButton;
    // 开始按钮
    private JButton startButton;
    private JTextArea outputArea;

    /**
     * 图形化的标签，和显示的相关信息
     */
    public MemoryManagerGUI() {
        setTitle("Memory Manager");
        setSize(600, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        initUI();
    }

    /**
     * 初始化 GUI 图形化
     */
    private void initUI() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(5, 2));

        JLabel inputLabel = new JLabel("输入文件:");
        inputFilePathField = new JTextField();
        selectInputFileButton = new JButton("选择文件");
        selectInputFileButton.addActionListener(new ActionListener() {
            /**
             * Java Swing应用程序中的事件处理方法，用于处理文件选择对话框的打开操作。
             * 具体来说，当用户点击一个按钮（假设这个按钮已经与这个事件处理方法关联）时，
             * 会弹出一个文件选择对话框，让用户选择一个文件。如果用户选择了文件并点击了“打开”按钮，
             * 那么这个文件的全路径名就会被设置到一个文本字段（inputFilePathField）中。
             * 这个文本字段通常用于显示用户选择的文件路径，以便用户可以知道他们选择了哪个文件。
             */
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                int result = fileChooser.showOpenDialog(MemoryManagerGUI.this);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File selectedFile = fileChooser.getSelectedFile();
                    inputFilePathField.setText(selectedFile.getAbsolutePath());
                }
            }
        });

        JLabel outputLabel = new JLabel("输出文件:");
        outputFilePathField = new JTextField();
        selectOutputFileButton = new JButton("选择文件");
        selectOutputFileButton.addActionListener(new ActionListener() {
            /**
             * Java Swing应用程序中的事件处理方法，用于处理文件选择对话框的打开操作。
             * 具体来说，当用户点击一个按钮（假设这个按钮已经与这个事件处理方法关联）时，
             * 会弹出一个文件选择对话框，让用户选择一个文件。如果用户选择了文件并点击了“打开”按钮，
             * 那么这个文件的全路径名就会被设置到一个文本字段（inputFilePathField）中。
             * 这个文本字段通常用于显示用户选择的文件路径，以便用户可以知道他们选择了哪个文件。
             */
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                int result = fileChooser.showSaveDialog(MemoryManagerGUI.this);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File selectedFile = fileChooser.getSelectedFile();
                    outputFilePathField.setText(selectedFile.getAbsolutePath());
                }
            }
        });

        startButton = new JButton("开始处理");
        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                processFiles();
            }
        });

        // 对 GUI 图形的相关处理
        outputArea = new JTextArea();
        outputArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(outputArea);

        panel.add(inputLabel);
        panel.add(inputFilePathField);
        panel.add(selectInputFileButton);
        panel.add(new JLabel()); // 占位符
        panel.add(outputLabel);
        panel.add(outputFilePathField);
        panel.add(selectOutputFileButton);
        panel.add(new JLabel()); // 占位符
        panel.add(startButton);

        add(panel, BorderLayout.NORTH);
        add(scrollPane, BorderLayout.CENTER);
    }

    /**
     * 对文件路径的处理和判断
     */
    private void processFiles() {
        String inputFilePath = inputFilePathField.getText();
        String outputFilePath = outputFilePathField.getText();

        if (inputFilePath.isEmpty() || outputFilePath.isEmpty()) {
            JOptionPane.showMessageDialog(this, "请输入文件路径！", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }

        File inputFile = new File(inputFilePath);
        if (!inputFile.exists()) {
            JOptionPane.showMessageDialog(this, "输入文件不存在！", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }

        try (BufferedReader input = new BufferedReader(new FileReader(inputFile));
             PrintWriter output = new PrintWriter(new FileWriter(outputFilePath))) {
            /**
             * 动态分区分配
             * 模拟内存管理。它从一个输入流（input）中读取命令，并根据这些命令执行相应的内存管理操作。
             * 这些命令包括添加分区（addPartition）、最佳适应分配（bestFitAllocate）、
             * 首次适应分配（firstFitAllocate）、最坏适应分配（worstFitAllocate）、
             * 释放分区（freePartition）和显示内存状态（displayMemory）。
             */
            MemoryManager memoryManager = new MemoryManager(output);
            String line;
            while ((line = input.readLine()) != null) {
                if (line.startsWith("addPartition")) {
                    int size = Integer.parseInt(line.split(" ")[1]);
                    memoryManager.addPartition(size);
                } else if (line.startsWith("bestFitAllocate")) {
                    int requestSize = Integer.parseInt(line.split(" ")[1]);
                    memoryManager.bestFitAllocate(requestSize);
                } else if (line.startsWith("firstFitAllocate")) {
                    int requestSize = Integer.parseInt(line.split(" ")[1]);
                    memoryManager.firstFitAllocate(requestSize);
                } else if (line.startsWith("worstFitAllocate")) {
                    int requestSize = Integer.parseInt(line.split(" ")[1]);
                    memoryManager.worstFitAllocate(requestSize);
                } else if (line.startsWith("freePartition")) {
                    int index = Integer.parseInt(line.split(" ")[1]);
                    memoryManager.freePartition(index);
                } else if (line.startsWith("displayMemory")) {
                    memoryManager.displayMemory();
                }
            }

            // 分页地址变换
            output.println("\n--- Page Table ---");
            PageTable pageTable = new PageTable(10, output);
            pageTable.setMapping(0, 5);  // Virtual page 0 maps to Physical page 5
            pageTable.setMapping(1, 3);  // Virtual page 1 maps to Physical page 3

            int virtualAddress = 8192;  // Example virtual address
            int physicalAddress = pageTable.getPhysicalAddress(virtualAddress);
            if (physicalAddress != -1) {
                output.println("Physical address for virtual address " + virtualAddress + ": " + physicalAddress);
            }

            // 页面置换算法 (FIFO)
            output.println("\n--- Page Replacement (FIFO) ---");
            PageReplacement pageReplacementFIFO = new PageReplacementFIFO(3, output);
            int[] pageRequests = {1, 2, 3, 1, 4, 2, 5};

            for (int page : pageRequests) {
                pageReplacementFIFO.requestPage(page);
            }

            pageReplacementFIFO.report();

            // 最优置换算法
            output.println("\n--- Page Replacement (OPT) ---");
            PageReplacement pageReplacementOPT = new PageReplacementOPT(3, output);
            for (int page : pageRequests) {
                pageReplacementOPT.requestPage(page);
            }
            pageReplacementOPT.report();

            // 最近最少使用 (LRU) 置换算法
            output.println("\n--- Page Replacement (LRU) ---");
            PageReplacement pageReplacementLRU = new PageReplacementLRU(3, output);
            for (int page : pageRequests) {
                pageReplacementLRU.requestPage(page);
            }
            pageReplacementLRU.report();

            JOptionPane.showMessageDialog(this, "处理完成！", "成功", JOptionPane.INFORMATION_MESSAGE);
            outputArea.setText(""); // Clear the output area
            try (BufferedReader reader = new BufferedReader(new FileReader(outputFilePath))) {
                String outputLine;
                while ((outputLine = reader.readLine()) != null) {
                    outputArea.append(outputLine + "\n");
                }
            }

        } catch (IOException e) {
            JOptionPane.showMessageDialog(this, "读取文件时发生错误: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            MemoryManagerGUI gui = new MemoryManagerGUI();
            gui.setVisible(true);
        });
    }
}

class Partition {
    int size;
    boolean isAllocated;

    public Partition(int size) {
        this.size = size;
        this.isAllocated = false;
    }
}

/**
 * 内存管理类
 */
class MemoryManager {
    ArrayList<Partition> partitions;
    PrintWriter output;

    /**
     * 内存管理
     * @param output 内存管理的相关的方式的处理结果
     */
    public MemoryManager(PrintWriter output) {
        partitions = new ArrayList<>();
        this.output = output;
    }

    /**
     * 添加分区
     * @param size 内存大小
     */
    public void addPartition(int size) {
        partitions.add(new Partition(size));
        output.println("Added partition of size " + size);
    }

    // 首次适应分配
    public void firstFitAllocate(int requestSize) {
        for (int i = 0; i < partitions.size(); i++) {
            Partition partition = partitions.get(i);
            if (!partition.isAllocated && partition.size >= requestSize) {
                partition.isAllocated = true;
                output.println("Allocated " + requestSize + " bytes to partition " + i);
                return;
            }
        }
        output.println("Allocation failed for " + requestSize);
    }

    // 最佳适配算法
    public void bestFitAllocate(int requestSize) {
        int bestIndex = -1;
        int bestSize = Integer.MAX_VALUE;

        for (int i = 0; i < partitions.size(); i++) {
            Partition partition = partitions.get(i);
            if (!partition.isAllocated && partition.size >= requestSize) {
                if (partition.size < bestSize) {
                    bestSize = partition.size;
                    bestIndex = i;
                }
            }
        }
        if (bestIndex != -1) {
            partitions.get(bestIndex).isAllocated = true;
            output.println("Allocated " + requestSize + " bytes to partition " + bestIndex);
        } else {
            output.println("Allocation failed for " + requestSize);
        }
    }

    // 最差适应算法
    public void worstFitAllocate(int requestSize) {
        int worstIndex = -1;
        int worstSize = -1;

        for (int i = 0; i < partitions.size(); i++) {
            Partition partition = partitions.get(i);
            if (!partition.isAllocated && partition.size >= requestSize) {
                if (partition.size > worstSize) {
                    worstSize = partition.size;
                    worstIndex = i;
                }
            }
        }
        if (worstIndex != -1) {
            partitions.get(worstIndex).isAllocated = true;
            output.println("Allocated " + requestSize + " bytes to partition " + worstIndex);
        } else {
            output.println("Allocation failed for " + requestSize);
        }
    }

    // 释放分区
    public void freePartition(int index) {
        if (index >= 0 && index < partitions.size()) {
            partitions.get(index).isAllocated = false;
            output.println("Freed partition " + index);
            mergePartition();
        } else {
            output.println("Invalid partition index");
        }
    }

    // 合并空闲分区
    private void mergePartition() {
        ArrayList<Partition> mergedPartitions = new ArrayList<>();
        int i = 0;

        while (i < partitions.size()) {
            if (!partitions.get(i).isAllocated) {
                int size = 0;
                while (i < partitions.size() && !partitions.get(i).isAllocated) {
                    size += partitions.get(i).size;
                    i++;
                }
                mergedPartitions.add(new Partition(size));
            } else {
                mergedPartitions.add(partitions.get(i));
                i++;
            }
        }
        partitions = mergedPartitions;
    }

    // 显示内存分区
    public void displayMemory() {
        output.println("Memory Partitions:");
        for (int i = 0; i < partitions.size(); i++) {
            Partition partition = partitions.get(i);
            String status = partition.isAllocated ? "Allocated" : "Free";
            output.println("Partition " + i + ": Size=" + partition.size + ", Status=" + status);
        }
    }
}

// 分页表
class PageTable {
    int[] table;
    PrintWriter output;

    public PageTable(int size, PrintWriter output) {
        table = new int[size];
        for (int i = 0; i < size; i++) {
            table[i] = -1;
        }
        this.output = output;
    }

    public void setMapping(int virtualPage, int physicalPage) {
        table[virtualPage] = physicalPage;
    }

    public int getPhysicalAddress(int virtualAddress) {
        int pageSize = 4096;
        int virtualPage = virtualAddress / pageSize;
        int offset = virtualAddress % pageSize;
        int physicalPage = table[virtualPage];

        if (physicalPage == -1) {
            output.println("Page fault");
            return -1;
        }
        return physicalPage * pageSize + offset;
    }
}

// 内存管理器分页管理
abstract class PageReplacement {
    int numFrames;
    int pageFaults;
    PrintWriter output;

    // 分页管理
    public PageReplacement(int numFrames, PrintWriter output) {
        this.numFrames = numFrames;
        this.pageFaults = 0;
        this.output = output;
    }

    public abstract void requestPage(int page);

    public void report() {
        output.println("Total page faults: " + pageFaults);
    }
}

class PageReplacementFIFO extends PageReplacement {
    Queue<Integer> frames;

    public PageReplacementFIFO(int numFrames, PrintWriter output) {
        super(numFrames, output);
        frames = new LinkedList<>();
    }

    @Override
    public void requestPage(int page) {
        if (!frames.contains(page)) {
            if (frames.size() < numFrames) {
                frames.add(page);
            } else {
                int replacedPage = frames.poll();
                frames.add(page);
                output.println("Page " + replacedPage + " replaced with " + page);
            }
            pageFaults++;
        } else {
            output.println("Page " + page + " already in memory");
        }
    }
}

// 最优页面替换策略
class PageReplacementOPT extends PageReplacement {
    ArrayList<Integer> frames;

    public PageReplacementOPT(int numFrames, PrintWriter output) {
        super(numFrames, output);
        frames = new ArrayList<>();
    }

    @Override
    public void requestPage(int page) {
        if (!frames.contains(page)) {
            if (frames.size() < numFrames) {
                frames.add(page);
            } else {
                // Find the optimal page to replace
                int farthestIndex = -1;
                int pageToReplace = -1;
                for (Integer frame : frames) {
                    int index = findFarthestUsage(frame);
                    if (index > farthestIndex) {
                        farthestIndex = index;
                        pageToReplace = frame;
                    }
                }
                frames.remove(pageToReplace);
                frames.add(page);
                output.println("Page " + pageToReplace + " replaced with " + page);
            }
            pageFaults++;
        } else {
            output.println("Page " + page + " already in memory");
        }
    }

    private int findFarthestUsage(int page) {
        // Return the index of the farthest usage of the page
        // Placeholder logic, needs to be implemented properly based on request sequence
        return 0; // Replace with actual logic to find the farthest index
    }
}


class PageReplacementLRU extends PageReplacement {
    ArrayList<Integer> frames;
    ArrayList<Integer> pageOrder;

    public PageReplacementLRU(int numFrames, PrintWriter output) {
        super(numFrames, output);
        frames = new ArrayList<>();
        pageOrder = new ArrayList<>();
    }

    @Override
    public void requestPage(int page) {
        if (!frames.contains(page)) {
            if (frames.size() < numFrames) {
                frames.add(page);
            } else {
                int lruPage = pageOrder.remove(0); // Remove the least recently used page
                frames.remove(lruPage);
                frames.add(page);
                output.println("Page " + lruPage + " replaced with " + page);
            }
            pageOrder.add(page);
            pageFaults++;
        } else {
            output.println("Page " + page + " already in memory");
            pageOrder.remove((Integer) page); // Update the order
            pageOrder.add(page);
        }
    }
}
